package photorganizer.common.bean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

public class DynamicLocator extends LocatorAbstract {

	private static class DynamicHandler implements InvocationHandler {

		private final Object bean;

		private final List<Interceptor> interceptors;

		public DynamicHandler(Object bean, List<Interceptor> interceptors) {
			this.bean = bean;
			this.interceptors = interceptors;
		}

		/* (non-Javadoc)
		 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
		 */
		public Object invoke(Object proxy, Method method, Object[] parameters) throws Throwable {
			Request request = new Request(proxy, bean, method, parameters, interceptors);
			return request.invoke();
		}

	}

	private final List<Interceptor> locatorInterceptors;

	public DynamicLocator(Locator delegate, Properties properties) {
		super(delegate, properties);

		locatorInterceptors = new ArrayList<Interceptor>();

		String property = getProperty("interceptors");
		if (property != null) {
			StringTokenizer tokenizer = new StringTokenizer(property, ", ");

			while (tokenizer.hasMoreTokens()) {
				Interceptor interceptor = get(Interceptor.class, tokenizer.nextToken());
				locatorInterceptors.add(interceptor);
			}
		}
	}

	@Override
	public <T> T get(Class<T> beanClass) {
		T bean = super.get(beanClass);

		if (!beanClass.isInterface()) {
			return bean;
		}

		List<Interceptor> interceptors = new ArrayList<Interceptor>(locatorInterceptors);
		addBeanInterceptors(interceptors, beanClass);

		if (interceptors.isEmpty()) {
			return bean;
		}

		InvocationHandler invocationHandler = new DynamicHandler(bean, interceptors);
		Object proxy = Proxy.newProxyInstance(beanClass.getClassLoader(), new Class[] { beanClass }, invocationHandler);
		return beanClass.cast(proxy);
	}

	private boolean addBeanInterceptors(List<Interceptor> interceptors, Class<?> beanInterface) {
		Interceptors annotation = beanInterface.getAnnotation(Interceptors.class);

		if (annotation == null) {
			for (Class<?> superInterface : beanInterface.getInterfaces()) {
				if (addBeanInterceptors(interceptors, superInterface)) {
					return true;
				}
			}
		}
		else {
			for (Class<? extends Interceptor> interceptorClass : annotation.value()) {
				Interceptor interceptor = super.get(interceptorClass);
				interceptors.add(interceptor);
			}
			return true;
		}

		return false;
	}

}